package Kernel;

import java.io.IOException;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.GregorianCalendar;


public class Serveur implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private String ip;
	private String port;
	private ArrayList<Trajet> trajets;
	private ArrayList<Ville> villes;
	private ArrayList<Vehicule> vehicules;
	private ArrayList<String> logs;
	private ArrayList<Billet> billets;
	private ArrayList<Client> clients;
	private Reseau reseau;

	/* #### Constructeurs #### */

	/**
	 * Constructeur par defaut du serveur. L'addresse IP et le port par defaut
	 * sont localhost:444.
	 */
	public Serveur() {
		this.ip = "127.0.0.1";
		this.port = "444";
		this.trajets = new ArrayList<Trajet>();
		this.villes = new ArrayList<Ville>();
		this.vehicules = new ArrayList<Vehicule>();
		this.logs = new ArrayList<String>();
		this.billets=new ArrayList<Billet>();
		this.clients=new ArrayList<Client>();
	}

	/**
	 * Constructeur de la classe Serveur
	 * 
	 * @param ip
	 *            est l'addresse IP du serveur.
	 * @param port
	 *            est le port de communication dédié sur le serveur.
	 */
	public Serveur(String ip, String port) {
		this.ip = ip;
		this.port = port;
		this.trajets = new ArrayList<Trajet>();
		this.villes = new ArrayList<Ville>();
		this.vehicules = new ArrayList<Vehicule>();
		this.logs = new ArrayList<String>();
		this.billets=new ArrayList<Billet>();
		this.clients=new ArrayList<Client>();
	}

	/**
	 * Constructeur paramétré de la classe Serveur.
	 * 
	 * @param ip
	 *            est l'IP du serveur
	 * @param port
	 *            est le port de communication
	 * @param trajets
	 *            est une ArrayList contenant les trajets
	 * @param villes
	 *            est une ArrayList contenant les villes
	 * @param vehicules
	 *            est une ArrayList contenant les véhicules
	 * @param  billets
	 * 			  est une ArrayList contenant les billets
	 * @param clients
	 * 				est une ArrayList contenant des clients
	 * @param logs
	 *            est une ArrayList contenant les logs.
	 */
	public Serveur(String ip, String port, ArrayList<Trajet> trajets,
			ArrayList<Ville> villes, ArrayList<Vehicule> vehicules, ArrayList<Billet> billets, ArrayList<Client> clients,
			ArrayList<String> logs) {
		this.ip = ip;
		this.port = port;
		this.trajets = trajets;
		this.villes = villes;
		this.vehicules = vehicules;
		this.billets=billets;
		this.clients=clients;
		this.logs = logs;
	}

	/**
	 * Constructeur par recopie
	 * 
	 * @param serveur
	 *            est le serveur à recopier dans l'objet courant.
	 */
	public Serveur(Serveur serveur) {
		this.ip = serveur.getIp();
		this.port = serveur.getPort();
		this.logs = serveur.getLogs();
		this.trajets = serveur.getTrajets();
		this.vehicules = serveur.getVehicules();
		this.villes = serveur.getVilles();
		this.billets=serveur.getBillets();
		this.clients=serveur.getClients();
	}

	/* #### Methodes definies manuellement #### */

	/**
	 * Ajouter un trajet à la liste
	 * 
	 * @param trajet
	 *            trajet à ajouter
	 */
	public void ajouterTrajet(Trajet trajet) {
		this.trajets.add(trajet);
		this.ajouterLog("Ajout du trajet " + trajet.toString() + "\n");
	}

	/**
	 * Supprimer un trajet de la liste
	 * 
	 * @param trajet
	 *            est le trajet à supprimer
	 * @return true si la suppression est effectuée , false sinon.
	 */
	public boolean supprimerTrajet(Trajet trajet) {
		int reference = this.trajets.size();
		this.trajets.remove(trajet);
		this.ajouterLog("Suppression du trajet " + trajet.toString() + "\n");
		return this.trajets.size() == reference - 1;
	}

	/**
	 * Ajouter un vehicule à la liste
	 * 
	 * @param vehicule
	 *            vehicule à ajouter
	 */
	public void ajouterVehicule(Vehicule vehicule) {
		this.vehicules.add(vehicule);
		this.ajouterLog("Ajout du vehicule " + vehicule.toString() + "\n");
	}

	/**
	 * Supprimer un vehicule de la liste
	 * 
	 * @param vehicule
	 *            est le vehicule à supprimer
	 * @return true si la suppression est effectuée , false sinon.
	 */
	public boolean supprimerVehicule(Vehicule vehicule) {
		int reference = this.vehicules.size();
		this.vehicules.remove(vehicule);
		this
				.ajouterLog("Suppression du vehicule " + vehicule.toString()
						+ "\n");
		return this.vehicules.size() == reference - 1;
	}

	/**
	 * Ajouter une ville à la liste
	 * 
	 * @param ville
	 *            ville à ajouter
	 */
	public void ajouterVille(Ville ville) {
		this.villes.add(ville);
		this.ajouterLog("Ajout de la ville " + ville.getNom() + "\n");
	}

	/**
	 * Supprimer une ville de la liste
	 * 
	 * @param ville
	 *            est la ville à supprimer
	 * @return true si la suppression est effectuée , false sinon.
	 */
	public boolean supprimerVille(Ville ville) {
		int reference = this.villes.size();
		this.villes.remove(ville);
		this.ajouterLog("Suppression de la ville " + ville.getNom() + "\n");
		return this.villes.size() == reference - 1;
	}

	/**
	 * Ajouter un Billet à la liste
	 * 
	 * @param billet
	 * 				billet à ajouter
	 */
	
	public void ajouterBillet(Billet billet){
		this.billets.add(billet);
		this.ajouterLog("Ajout du billet"+billet.toString()+"\n");
	}
	
	/**
	 * Suppression d'un Billet de la liste
	 * 
	 * @param billet
	 * 				billet à supprimer
	 * 
	 * @return true si la suppression est effectuée , false sinon.
	 */
	public boolean supprimerBillet(Billet billet){
		int reference = this.billets.size();
		this.billets.remove(billet);
		this.ajouterLog("Suppression du billet " + billet.toString() + "\n");
		return this.billets.size() == reference - 1;
		
	}
	/**
	 * Ajouter un client à la liste
	 * 
	 * @param client
	 * 				client à ajouter
	 */
	
	public void ajouterClient(Client client){
		this.clients.add(client);
		this.ajouterLog("Ajout du client"+client.toString()+"\n");
	}
	
	/**
	 * Suppression d'un client de la liste
	 * 
	 * @param client
	 * 				client à supprimer
	 * 
	 * @return true si la suppression est effectuée , false sinon.
	 */
	public boolean supprimerClient(Client client){
		int reference = this.clients.size();
		this.clients.remove(client);
		this.ajouterLog("Suppression du client " + client.toString() + "\n");
		return this.clients.size() == reference - 1;
		
	}
	
	
	/**
	 * Formatter l'objet pour la sauvegarde.
	 * 
	 * @return contient les informations sur l'objet , séparés par des ";"
	 */
	public String print() {
		String accu = "";
		accu = accu + this.ip + ";" + this.port + ";";
		for (int i = 0; i < this.trajets.size(); i++) {
			accu = accu + this.trajets.get(i).print() + "\n";
		}
		for (int j = 0; j < this.vehicules.size(); j++) {
			accu = accu + this.vehicules.get(j).print() + "\n";
		}
		for (int k = 0; k < this.villes.size(); k++) {
			accu = accu + this.villes.get(k).print() + "\n";
		}
		for (int k = 0; k < this.billets.size(); k++) {
			accu = accu + this.billets.get(k).print() + "\n";
		}
		return accu;
	}

	/**
	 * Ajouter une ligne de log à la liste
	 * 
	 * @param log
	 *            log à ajouter
	 */
	public void ajouterLog(String log) {
		this.logs.add(log);
	}

	/**
	 * Effectuer une sauvegarde
	 * 
	 * @param version
	 *            est la version de sauvegarde à utiliser (V1 ou V2)
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public void sauvegarder(String version) throws IOException,
			ClassNotFoundException {
		if (version.compareTo("V1") == 0)
			v1.Sauvegarde.sauvegarder(this);
		else
			v2.Sauvegarde.Sauvegarder(this);
	}

	/**
	 * Restaurer
	 * 
	 * @param version
	 *            est la version du chargement à utiliser (V1 ou V2)
	 * @return un nouvel objet serveur
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public Serveur charger(String version) throws IOException,
			ClassNotFoundException {
		if (version.compareTo("V1") == 0)
			return v1.Chargement.Charger(this);
		else
			v2.Chargement.Charger(this);
		return this;
	}

	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringVehicules() {
		String accu = "";
		for (int i = 0; i < this.vehicules.size(); i++) {
			accu += this.vehicules.get(i).toString()
					+ "\n-------------------------------\n";
		}
		return accu;
	}

	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringVilles() {
		String accu = "";
		for (int i = 0; i < this.villes.size(); i++) {
			accu += this.villes.get(i).toString()
					+ "\n-------------------------------\n";
		}
		return accu;
	}

	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringTrajet() {
		String accu = "";
		for (int i = 0; i < this.trajets.size(); i++) {
			accu += this.trajets.get(i).toString()
					+ "\n-------------------------------\n";
		}
		return accu;
	}
	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringBillet() {
		String accu = "";
		for (int i = 0; i < this.billets.size(); i++) {
			accu += this.billets.get(i).toString()
					+ "\n-------------------------------\n";
		}
		return accu;
	}
	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringClient() {
		String accu = "";
		for (int i = 0; i < this.clients.size(); i++) {
			accu += this.clients.get(i).toString()
					+ "\n-------------------------------\n";
		}
		return accu;
	}
	
	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return un billet dans un format plus agréable
	 */
	public String toStringUnBillet(int numerosBillet) {
		String accu = "";
			accu += this.billets.get(numerosBillet).toString()
					+ "\n-------------------------------\n";
		return accu;
	}
	

	/**
	 * Formatte l'affichage pour le GUI
	 * 
	 * @return l'arrayList dans un format plus agréable
	 */
	public String toStringLogs() {
		String accu = "";
		for (int i = 0; i < this.logs.size(); i++) {
			accu += this.logs.get(i).toString()
					+ "-------------------------------\n";
		}
		return accu;
	}

	/**
	 * Vérifie si le véhicule est déjà contenu dans la liste (Test par reference
	 * uniquement)
	 * 
	 * @param vehicule
	 *            est le véhicule à tester
	 * @return true si le véhicule existe déjà , false sinon
	 */
	public boolean existeDejaVehicule(Vehicule vehicule) {
		boolean b = false;
		for (int i = 0; i < vehicules.size(); i++) {
			if (this.vehicules.get(i).getDenomination().compareTo(
					vehicule.getDenomination()) == 0)
				return true;
		}
		return b;
	}

	/**
	 * Vérifie si la ville est déjà contenue dans la liste (Test par nom
	 * uniquement)
	 * 
	 * @param ville
	 *            est la ville à tester
	 * @return true si la ville existe déjà , false sinon
	 */
	public boolean existeDejaVille(Ville ville) {
		boolean b = false;
		for (int i = 0; i < villes.size(); i++) {
			if (this.villes.get(i).getNom().compareTo(ville.getNom()) == 0)
				return true;
		}
		return b;
	}

	/**
	 * Vérifie si le trajet est déjà contenu dans la liste (Test par reference
	 * uniquement)
	 * 
	 * @param trajet
	 *            est le trajet à tester
	 * @return true si le trajet existe déjà , false sinon
	 */
	public boolean existeDejaTrajet(Trajet trajet) {
		boolean b = false;
		for (int i = 0; i < trajets.size(); i++) {
			if (this.trajets.get(i).getReference().compareTo(
					trajet.getReference()) == 0)
				return true;
		}
		return b;
	}

	/**
	 * Rechercher et retourner une ville
	 * 
	 * @param reference
	 *            est le nom de la ville que vous cherchez
	 * @return renvoye une ville si elle existe, et null sinon.
	 */
	public Ville chercherVille(String reference) {
		for (int i = 0; i < this.villes.size(); i++) {
			if (this.villes.get(i).getNom().compareTo(reference) == 0) {
				return this.villes.get(i);
			}
		}
		return null;
	}

	/**
	 * Rechercher et retourner un véhicule
	 * 
	 * @param reference
	 *            est la référence du véhicule que vous cherchez
	 * @return renvoye le véhicule s'il existe, et null sinon.
	 */
	public Vehicule chercherVehicule(String reference) {
		for (int i = 0; i < this.vehicules.size(); i++) {
			if (this.vehicules.get(i).getDenomination().compareTo(reference) == 0) {
				return this.vehicules.get(i);
			}
		}
		return null;
	}

	/**
	 * Rechercher et retourner un trajet
	 * 
	 * @param reference
	 *            est la référence du trajet que vous cherchez
	 * @return renvoye un trajet s'il existe, et null sinon.
	 */
	public Trajet chercherTrajet(String reference) {
		for (int i = 0; i < this.trajets.size(); i++) {
			if (this.trajets.get(i).getReference().compareTo(reference) == 0) {
				return this.trajets.get(i);
			}
		}
		return null;
	}

	/**
	 * parseur d'un objet (String) à vers un objet (Horaire) si c'est possible
	 * 
	 * @param GregorianCalendar
	 *            est le String contenant l'horaire
	 * @return est l'Horaire.
	 * 
	 */
	public GregorianCalendar hashtoHoraire(String horaire) {
		String[] buff = horaire.split("\n");
		String[] buffHoraires = buff[0].split(":");
		String[] buffDate = buff[1].split("/");
		// return new Horaire(new
		// Horaires(Integer.parseInt(buffHoraires[0]),Integer.parseInt(buffHoraires[1]),0),new
		// Date(Integer.parseInt(buffDate[0]),Integer.parseInt(buffDate[1]),Integer.parseInt(buffDate[2])));
		return new GregorianCalendar(Integer.parseInt(buffDate[2]), Integer
				.parseInt(buffDate[1]), Integer.parseInt(buffDate[0]), Integer
				.parseInt(buffHoraires[0]), Integer.parseInt(buffHoraires[1]));
	}

	/* |||| Méthodes de recherche |||| */

	/**
	 * Rechercher un trajet par la ville
	 * 
	 * @param v
	 *            est la ville que vous souhaitez chercher
	 * @return une ArrayList contenant les trajets reliés à cette ville
	 */
	public ArrayList<Trajet> rechercherTrajetVille(Ville v) {
		ArrayList<Trajet> t = new ArrayList<Trajet>();
		for (int i = 0; i < this.trajets.size(); i++) {
			if (this.trajets.get(i).getVilleArrivee().getNom().compareTo(
					v.getNom()) == 0
					|| this.trajets.get(i).getVilleDepart().getNom().compareTo(
							v.getNom()) == 0) {
				t.add(this.trajets.get(i));
			}
		}
		return t;
	}

	/**
	 * Rechercher un trajet par le véhicule
	 * 
	 * @param v
	 *            est le vehicule que vous souhaitez rechercher
	 * @return une ArrayList contenant les trajets reliés à ce véhicule
	 */
	public ArrayList<Trajet> rechercherTrajetVehicule(Vehicule v) {
		ArrayList<Trajet> t = new ArrayList<Trajet>();
		for (int i = 0; i < this.trajets.size(); i++) {
			if (this.trajets.get(i).getVehicule().getDenomination().compareTo(
					v.getDenomination()) == 0) {
				t.add(this.trajets.get(i));
			}
		}
		return t;
	}

	/* #### Methodes surchargees #### */

	/* #### Mutateurs #### */

	public ArrayList<Trajet> getTrajets() {
		return trajets;
	}

	public ArrayList<String> getLogs() {
		return logs;
	}

	public String getIp() {
		return ip;
	}

	public void setIp(String ip) {
		this.ip = ip;
	}

	public String getPort() {
		return port;
	}

	public void setPort(String port) {
		this.port = port;
	}

	public void setTrajets(ArrayList<Trajet> trajets) {
		this.trajets = trajets;
	}

	public ArrayList<Ville> getVilles() {
		return villes;
	}

	public void setVilles(ArrayList<Ville> villes) {
		this.villes = villes;
	}

	public ArrayList<Vehicule> getVehicules() {
		return vehicules;
	}

	public void setVehicules(ArrayList<Vehicule> vehicules) {
		this.vehicules = vehicules;
	}

	public ArrayList<Billet> getBillets() {
		return billets;
	}

	public void setBillets(ArrayList<Billet> billets) {
		this.billets = billets;
	}
	

	public ArrayList<Client> getClients() {
		return clients;
	}

	public void setClients(ArrayList<Client> clients) {
		this.clients = clients;
	}

	public Reseau getReseau() {
		return reseau;
	}

	public void initReseau() {
		reseau = new Reseau(villes, trajets);
	}

}
